﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class ShadowCamera : MonoBehaviour
{
//	public float shadowDistance = 5;
//	float oldDistance;

	public Camera renderCamera;
	public Shader shader;
	public int resolution = 512;

	Camera shadowCamera;
	CommandBuffer enableKeywordCmd;
	CommandBuffer disableKeywordCmd;
	CommandBuffer shadowMapCmd;
	CommandBuffer matrixCmd;
	Matrix4x4 oldProjectMatrix;
	RenderTexture shadowRT;

//	void OnEnable ()
//	{
//		oldDistance = QualitySettings.shadowDistance;
//		QualitySettings.shadowDistance = shadowDistance;
//	}
//
//	void OnDisable()
//	{
//		QualitySettings.shadowDistance = oldDistance;
//	}

	public void EnableShadow (bool enable)
	{
		if (!enable)
		{
			Disable();
			return;
		}
		
		if (enableKeywordCmd != null)
		{
			return;
		}
		
		if (resolution <= 0)
		{
			return;
		}
		
		if (renderCamera == null)
		{
			return;
		}

		Light light = this.GetComponent<Light>();
		if (light == null)
		{
			return;
		}

		shadowCamera = this.GetComponent<Camera>();
		if (shadowCamera == null)
		{
			return;
		}

		shadowCamera.aspect = 1;
		shadowCamera.clearFlags = CameraClearFlags.Color;
		shadowCamera.backgroundColor = Color.white;
		if (Application.platform == RuntimePlatform.IPhonePlayer)
		{
			shadowCamera.backgroundColor = Color.black;
		}
		shadowCamera.cullingMask = renderCamera.cullingMask;

		light.shadowCustomResolution = 1;


		shadowCamera.SetReplacementShader(shader, "RenderType");

		RenderTextureFormat rtf = RenderTextureFormat.RFloat;
		if (!SystemInfo.SupportsRenderTextureFormat(rtf) || (!Application.isEditor && Application.platform == RuntimePlatform.Android))
		{
			rtf = RenderTextureFormat.RGFloat;
			if (!SystemInfo.SupportsRenderTextureFormat(rtf) || (!Application.isEditor && Application.platform == RuntimePlatform.Android))
			{
				rtf = RenderTextureFormat.RHalf;
				if (!SystemInfo.SupportsRenderTextureFormat(rtf))
				{
					rtf = RenderTextureFormat.RGHalf;
					if (!SystemInfo.SupportsRenderTextureFormat(rtf))
					{
						rtf = RenderTextureFormat.ARGB2101010;
					}
				}
			}
		}



		shadowRT = new RenderTexture(resolution, resolution, 0, rtf);
		shadowRT.filterMode = FilterMode.Bilinear;
		shadowRT.wrapMode = TextureWrapMode.Clamp;
		shadowRT.name = "shadowRT";

		shadowCamera.targetTexture = shadowRT;

		StartCoroutine(AddCommandBuffer());
	}

	IEnumerator AddCommandBuffer()
	{
		yield return null;

        Debug.Log("add shadow command buffer");

		enableKeywordCmd = new CommandBuffer();
		enableKeywordCmd.name = "enableKeywordCmd";
		enableKeywordCmd.EnableShaderKeyword("SHADOW_MAP_NEW");
		renderCamera.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, enableKeywordCmd);

		shadowMapCmd = new CommandBuffer();
		shadowMapCmd.name = "shadowMapCmd";
		shadowMapCmd.SetGlobalTexture("ShadowMap", shadowRT);
		renderCamera.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, shadowMapCmd);

		disableKeywordCmd = new CommandBuffer();
		disableKeywordCmd.name = "disableKeywordCmd";
		disableKeywordCmd.DisableShaderKeyword("SHADOW_MAP_NEW");
		renderCamera.AddCommandBuffer(CameraEvent.AfterEverything, disableKeywordCmd);

		UpdateShadowMatrix();


	}

	void UpdateShadowMatrix()
	{
		if (shadowCamera == null)
		{
			return;
		}

		Matrix4x4 projMat = GL.GetGPUProjectionMatrix(shadowCamera.projectionMatrix, false);
		Matrix4x4 worldToShadowMat = projMat * shadowCamera.worldToCameraMatrix;

		Matrix4x4 clipToTexMat = Matrix4x4.identity;
		if (Application.platform == RuntimePlatform.IPhonePlayer)
		{
			clipToTexMat = Matrix4x4.Translate(new Vector3(0.5f, 0.5f, 0)) * Matrix4x4.Scale(new Vector3(0.5f, 0.5f, 1));
		}
		else
		{
			clipToTexMat = Matrix4x4.Translate(new Vector3(0.5f, 0.5f, 0.5f)) * Matrix4x4.Scale(new Vector3(0.5f, 0.5f, 0.5f));
		}

		worldToShadowMat = clipToTexMat * worldToShadowMat;
		if (oldProjectMatrix == worldToShadowMat)
		{
			return;
		}
		oldProjectMatrix = worldToShadowMat;

		if (matrixCmd == null)
		{
			matrixCmd = new CommandBuffer();
			matrixCmd.name = "matrixCmd";
			renderCamera.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, matrixCmd);
		}

		matrixCmd.Clear();
		matrixCmd.SetGlobalMatrix("WorldToShadow", worldToShadowMat);
	}

	void Disable()
	{
		if (enableKeywordCmd != null)
		{
			if (renderCamera != null)
			{
                Debug.Log("remove shadow command buffer");

				renderCamera.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, enableKeywordCmd);
				enableKeywordCmd.Dispose();
				enableKeywordCmd = null;

				renderCamera.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, shadowMapCmd);
				shadowMapCmd.Dispose();
				shadowMapCmd = null;

				renderCamera.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, matrixCmd);
				matrixCmd.Dispose();
				matrixCmd = null;

				renderCamera.RemoveCommandBuffer(CameraEvent.AfterEverything, disableKeywordCmd);
				disableKeywordCmd.Dispose();
				disableKeywordCmd = null;
			}
		}
	}

	void OnEnable()
	{
		EnableShadow(true);
	}

	void OnDisable()
	{
		EnableShadow(false);
	}

	void Update()
	{
//		if (this.transform.hasChanged)
//		{
			UpdateShadowMatrix();
//		}
	}
}
